package com.iwomy.envpro.platform.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.iwomy.envpro.platform.VO.AirRealTimeDataVO;
import com.iwomy.envpro.platform.VO.GisDetailVO;
import com.iwomy.envpro.platform.VO.GisVO;
import com.iwomy.envpro.platform.constant.CpEnum;
import com.iwomy.envpro.platform.constant.TpEnum;
import com.iwomy.envpro.platform.dto.AirRealTimeDataDTO;
import com.iwomy.envpro.platform.entity.*;
import com.iwomy.envpro.platform.mapper.AirRealTimeDataMapper;
import com.iwomy.envpro.platform.service.AirMonitorEquipmentService;
import com.iwomy.envpro.platform.service.AirRealTimeDataService;
import com.iwomy.envpro.platform.service.AirSiteService;
import com.iwomy.envpro.platform.utils.AqiUtil;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 空气实时数据表
 *
 * @author pig
 * @date 2023-08-31 09:28:08
 */
@Service
public class AirRealTimeDataServiceImpl extends ServiceImpl<AirRealTimeDataMapper, AirRealTimeDataEntity> implements AirRealTimeDataService {
	@Resource
	private AirSiteService airSiteService;
	@Resource
	private MongoTemplate mongoTemplate;
	@Resource
	private AirMonitorEquipmentService airMonitorEquipmentService;

	@Override
	public Page getAirRealTimeList(Page page, AirRealTimeDataDTO airRealTimeDataDTO) {
		Page airSiteList = airSiteService.getAirSitePage(page, airRealTimeDataDTO.getSiteId());
		List<AirSiteEntity> records = airSiteList.getRecords();
		List<Long> siteIdList = records.stream().map(AirSiteEntity::getId).collect(Collectors.toList());
		airRealTimeDataDTO.setSiteId(null);
		//多表查询
		MPJLambdaWrapper<AirRealTimeDataEntity> wrapper = new MPJLambdaWrapper<>();
		wrapper.selectAll(AirRealTimeDataEntity.class)
				.selectAs(AirSiteEntity::getName, AirRealTimeDataVO::getSiteName)
				.selectAs(AirSiteEntity::getLatitude, AirRealTimeDataVO::getLatitude)
				.selectAs(AirSiteEntity::getLongitude, AirRealTimeDataVO::getLongitude)
				.selectAs(AirSiteEntity::getMonitorType, AirRealTimeDataVO::getMonitorType)
				.leftJoin(AirSiteEntity.class, AirSiteEntity::getId, AirRealTimeDataEntity::getSiteId)
		;
		if (StrUtil.isNotEmpty(airRealTimeDataDTO.getSiteName())) {
			wrapper.like(AirSiteEntity::getName, airRealTimeDataDTO.getSiteName());
		}
		if (ObjectUtil.isEmpty(siteIdList)) {
			return new Page();
		}
		wrapper.in(AirRealTimeDataEntity::getSiteId, siteIdList);
		return this.getBaseMapper().selectJoinPage(page, AirRealTimeDataVO.class, wrapper);
	}

	@Override
	public List airRealTimeList(AirRealTimeDataDTO airRealTimeDataDTO) {
		List<AirSiteEntity> airSiteList = airSiteService.getAirSiteList(airRealTimeDataDTO.getSiteId());
		List<Long> siteIdList = airSiteList.stream().map(AirSiteEntity::getId).collect(Collectors.toList());
		LambdaQueryWrapper<AirMonitorEquipmentEntity> eq = Wrappers.<AirMonitorEquipmentEntity>lambdaQuery().eq(AirMonitorEquipmentEntity::getStatus, 2).in(AirMonitorEquipmentEntity::getAirSiteId, siteIdList);
		List<AirMonitorEquipmentEntity> airMonitorEquipmentEntities = airMonitorEquipmentService.list(eq);
		List<Long> airMonitorEquipmentIdList = airMonitorEquipmentEntities.stream().map(AirMonitorEquipmentEntity::getId).collect(Collectors.toList());
		//查询所有在线设备，获取所有在线设备
		MatchOperation matchOperation = Aggregation.match(Criteria.where("monitorEquipmentId").in(airMonitorEquipmentIdList));
		SortOperation sortOperation = Aggregation.sort(Sort.by(Sort.Direction.DESC, "recordTime"));
		GroupOperation groupOperation = Aggregation.group("monitorEquipmentId").first("$$ROOT").as("latestRecord");
		AggregationOperation replaceRootOperation = Aggregation.replaceRoot().withValueOf("$latestRecord");
		Aggregation aggregation = Aggregation.newAggregation(matchOperation, sortOperation, groupOperation, replaceRootOperation);

		AggregationResults<AirRealTimeDataEntity> results = mongoTemplate.aggregate(aggregation, "airRealTimeDataEntity", AirRealTimeDataEntity.class);
		List<AirRealTimeDataEntity> mappedResults = results.getMappedResults();
		List<AirRealTimeDataVO> airRealTimeDataVOS = BeanUtil.copyToList(mappedResults, AirRealTimeDataVO.class);
		airRealTimeDataVOS.stream().forEach(x -> {
			AirSiteEntity airSite = airSiteService.getById(x.getSiteId());
			x.setSiteName(airSite.getName());
			x.setLatitude(airSite.getLatitude());
			x.setLongitude(airSite.getLongitude());
		});
		return airRealTimeDataVOS;
	}

	@Override
	public GisVO airRealTimeByEquipmentId(AirRealTimeDataDTO airRealTimeDataDTO) {

		GisVO gisVO = new GisVO();
		List detail = new ArrayList<GisDetailVO>();

		AirMonitorEquipmentEntity airMonitorEquipmentEntity = airMonitorEquipmentService.getById(airRealTimeDataDTO.getMonitorEquipmentId());
		AirSiteEntity airSiteEntity = airSiteService.getById(airMonitorEquipmentEntity.getAirSiteId());
		gisVO.setAirSiteName(airSiteEntity.getName());
		gisVO.setLatitude(airMonitorEquipmentEntity.getLatitude());
		gisVO.setLongitude(airMonitorEquipmentEntity.getLongitude());
		gisVO.setAirSiteId(airMonitorEquipmentEntity.getAirSiteId());
		gisVO.setEquipmentId(airMonitorEquipmentEntity.getId());
//		Query query = Query.query(Criteria.where("monitorEquipmentId").is(airMonitorEquipmentEntity.getId()));
//		query.with(Sort.by(Sort.Direction.DESC, "recordTime"));
//		query.limit(1);

		//查询所有在线设备，获取所有在线设备
//		JSONObject jsonObject = mongoTemplate.findOne(query, JSONObject.class, TpEnum.REAl_TIME.getName());
		List<AirFactorEntity> airFactorEntities = airMonitorEquipmentService
				.getMonitorEquipmentFactor(airRealTimeDataDTO.getMonitorEquipmentId());
		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");
		for (AirFactorEntity factor : airFactorEntities) {
			String code = factor.getCode();
			po = po.and(code).as(code);

		}
		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").is(airRealTimeDataDTO.getMonitorEquipmentId())),
				Aggregation.sort(Sort.Direction.DESC, "recordTime"),
				Aggregation.limit(1),
				po
		);

		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, TpEnum.REAl_TIME.getName(), JSONObject.class);
		JSONObject jsonObject = results.getUniqueMappedResult();

		if (ObjectUtil.isEmpty(jsonObject)) return gisVO;

		gisVO.setRecordTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(jsonObject.getLongValue("recordTime")), ZoneId.systemDefault()));


		if (ObjectUtil.isNull(airFactorEntities)) return gisVO;


		for (AirFactorEntity x : airFactorEntities) {
			GisDetailVO gisDetailVO = BeanUtil.copyProperties(x, GisDetailVO.class);
			String code = x.getCode();
			JSONObject codeJson = jsonObject.getJSONObject(code);

			if (ObjectUtil.isNull(codeJson)) continue;
			BigDecimal val = codeJson.getBigDecimal("Rtd");
			String flag = jsonObject.getJSONObject(code).getString("Flag");
			gisDetailVO.setVal(val);
			gisDetailVO.setFlag(flag);
			detail.add(gisDetailVO);
		}

		try {
			gisVO.setAqi(getAQI(airRealTimeDataDTO));
		} catch (Exception e) {
			log.error("aqi 计算错误");
		}
		gisVO.setDetail(detail);
		return gisVO;
	}

	private Aqi getAQI(AirRealTimeDataDTO airRealTimeDataDTO) {
		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");

		po = po.and(CpEnum.SO2.getReportCode()).as(CpEnum.SO2.getReportCode());
		po = po.and(CpEnum.NO2.getReportCode()).as(CpEnum.NO2.getReportCode());
		po = po.and(CpEnum.PM10.getReportCode()).as(CpEnum.PM10.getReportCode());
		po = po.and(CpEnum.PM2_5.getReportCode()).as(CpEnum.PM2_5.getReportCode());
		po = po.and(CpEnum.CO.getReportCode()).as(CpEnum.CO.getReportCode());
		po = po.and(CpEnum.O3.getReportCode()).as(CpEnum.O3.getReportCode());

		Criteria recordTime = Criteria.where("recordTime")
				.gte(LocalDateTime.now().plusHours(-25).toInstant(ZoneOffset.of("+8")).toEpochMilli());


		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").is(airRealTimeDataDTO.getMonitorEquipmentId())),
				Aggregation.match(recordTime),
				Aggregation.sort(Sort.Direction.DESC, "recordTime"),
				Aggregation.limit(24),
				po
		);

		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, TpEnum.HOURLY_AVG.getName(), JSONObject.class);
		List<JSONObject> mappedResults = results.getMappedResults();
		double so2 = avgByCode(mappedResults, CpEnum.SO2);
		double no2 = avgByCode(mappedResults, CpEnum.NO2);
		double pm10 = avgByCode(mappedResults, CpEnum.PM10);
		double pm25 = avgByCode(mappedResults, CpEnum.PM2_5);
		double co = avgByCode(mappedResults, CpEnum.CO);
		double o3 = avgByCode(mappedResults, CpEnum.O3);
		Aqi aqi = AqiUtil.CountAqi(pm25, pm10, co, no2, o3, so2);
		int pollutionDegree = AqiUtil.getPollutionDegree(aqi.getAqi());
		String degree = AqiUtil.getDegree(pollutionDegree);
		aqi.setPollutionDegree(pollutionDegree);
		aqi.setDegree(degree);
		return aqi;
	}

	private double avgByCode(List<JSONObject> list, CpEnum cp) {

		return list.stream().mapToDouble(x -> {
			return x.getJSONObject(cp.getReportCode()).getDouble("Avg");
		}).average().orElse(0D);

	}

}